﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Transactions;
using Flower.Client;
using Flower.Directory.Host;
using Flower.Directory.Host.Default;
using Flower.Directory.Management;
using Flower.Security;
using Flower.Services;
using Flower.Services.Data;
using Flower.SqlServer.Directory;
using Flower.Workflow;
using NUnit.Framework;
using Flower.Directory.Util;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
using System.Threading;
using System.ServiceModel;
using SortOrder = Flower.Services.Data.SortOrder;
using Flower.Testing;
using Process = Flower.Services.Data.Process;

namespace Flower.Directory.Tests
{
    [TestFixture]
    public class DirectoryTests
    {
        private class TestAuthorizationProvider : IAuthorizationProvider
        {
            private readonly List<string> _roles = new List<string>();

            public IDirectory Directory
            {
                set { }
            }

            public string[] GetUserRoles(IIdentity identity)
            {
                return _roles.ToArray();
            }

            public void RefreshRoles()
            {
            }

            public TestAuthorizationProvider ClearRoles()
            {
                _roles.Clear();
                return this;
            }

            public TestAuthorizationProvider AddRole(string role)
            {
                _roles.Add(role);
                return this;
            }
        }

        private static readonly TransactionOptions TransactionOptions =
            new TransactionOptions
            {
                IsolationLevel = IsolationLevel.ReadCommitted,
                Timeout = TimeSpan.FromMinutes(1)
            };

        private static readonly IScriptEnginePool ScriptEnginePool = new ScriptEnginePool(1);
        private static readonly TestAuthorizationProvider AuthorizationProvider = new TestAuthorizationProvider();

        private bool _testAuthorization;

        [TestFixtureSetUp]
        public void Init()
        {
            _testAuthorization = false;
            //_testAuthorization = true;
        }

        private IDirectory CreateDirectory()
        {
            return new MemoryDirectory.Directory(ScriptEnginePool);
            /*return new SqlServer.Directory.Directory
            (
                new Settings { ConnectionStringName = "FlowerDirectory" },
                AuthorizationProvider,
                ScriptEnginePool
            );*/
        }

        #region DirectoryInit

        [Test]
        public void DirectoryInit()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            DirectoryEntry folder = dir.Get("/");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == string.Empty);

            folder = dir.Get("/Assemblies");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Assemblies");

            folder = dir.Get("/Services");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Services");

            folder = dir.Get("/Workflows");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Workflows");

            folder = dir.Get("/Processes");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Processes");

            folder = dir.Get("/Processors");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Processors");

            folder = dir.Get("/Sets");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Sets");

            folder = dir.Get("/Sets/Local");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Local");

            folder = dir.Get("/Sets/Shared");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Shared");

            folder = dir.Get("/Roles");
            Assert.IsTrue(folder != null);
            Assert.IsTrue(folder is Folder);
            Assert.IsTrue(folder.Name == "Roles");
        }

        #endregion

        #region MultilevelSaveGet

        [Test]
        public void MultilevelSaveGet()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Services")
                        .Folder("System1")
                            .Folder("Subsystem").End()
                        .End()
                        .Folder("System2").End()
                    .End();

                DirectoryEntry folder = dir.Get("/Services/System1");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "System1");
                Assert.IsTrue(dir.GetInfo("/Services/System1").Type == DirectoryEntryTypes.Folder);

                folder = dir.Get("/Services/System1/Subsystem");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "Subsystem");
                Assert.IsTrue(dir.GetInfo("/Services/System1/Subsystem").Type == DirectoryEntryTypes.Folder);

                folder = dir.Get("/Services/System2");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "System2");
                Assert.IsTrue(dir.GetInfo("/Services/System2").Type == DirectoryEntryTypes.Folder);
            }
        }

        #endregion

        #region Name collisions

        [Test]
        public void NameCollisions()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Services")
                        .Folder("System1")
                            .Folder("Subsystem").End()
                        .End()
                        .Folder("System2").End()
                    .End();

                DirectoryEntry folder = dir.Get("/Services/System1");

                dir.Save("/Services", folder);

                try
                {
                    dir.Save("/Services", new Folder { Name = "System1" });

                    Assert.Fail();
                }
                catch(FaultException<DirectoryFault>)
                {
                }
            }
        }

        #endregion

        #region Saving with the same parent

        [Test]
        public void SavingWithTheSameParent()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Services")
                        .Folder("System1")
                            .Folder("Subsystem").End()
                        .End()
                        .Folder("System2").End()
                    .End();

                DirectoryEntry folder = dir.Get("/Services/System1");

                folder.Name = "System3";

                dir.Save(null, folder);

                Assert.IsTrue(dir.Exists("/Services/System3", DateTime.MinValue));
            }
        }

        #endregion

        #region Sets

        [DataContract]
        public class MyMessage
        {
            [DataMember]
            public int Id { get; set; }

            [DataMember]
            public string Name { get; set; }
        }

        [Test]
        public void Sets()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set("MySet", typeof(MyMessage).FullName, 1)
                        .End()
                        .Set("AnotherSet", typeof(MyMessage).FullName, 1)
                        .End()
                    .End();

                var message = new MyMessage
                {
                    Id = 123,
                    Name = "Message"
                };

                string msgId = dir.Save
                (
                    "/Sets/Shared/MySet/Subset1/Subset2",
                    new Message
                    {
                        Name = "Message",
                        Data = message.ToBlob(BlobFormat.BinXml)
                    }
                );

                DirectoryEntry set = dir.Get("/Sets/Shared/MySet");
                Assert.IsTrue(set != null);
                Assert.IsTrue(set is Set);
                Assert.IsTrue(set.Name == "MySet");
                Assert.IsTrue(((Set)set).MessageType == typeof(MyMessage).FullName);
                Assert.IsTrue(((Set)set).Capacity == 1);

                DirectoryEntry folder = dir.Get("/Sets/Shared/MySet/Subset1");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "Subset1");

                folder = dir.Get("/Sets/Shared/MySet/Subset1/Subset2");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "Subset2");

                DirectoryEntry msg = dir.Get(msgId);
                Assert.IsTrue(msg != null);
                Assert.IsTrue(msg is Message);

                msg = dir.Get("/Sets/Shared/MySet/Subset1/Subset2/Message");
                Assert.IsTrue(msg != null);
                Assert.IsTrue(msg is Message);

                dir.Move(msgId, "/Sets/Shared/AnotherSet/Subset");

                Assert.IsTrue(dir.Get("/Sets/Shared/MySet/Subset1/Subset2/Message") == null);
                Assert.IsTrue(dir.Get("/Sets/Shared/MySet/Subset1/Subset2") == null);
                Assert.IsTrue(dir.Get("/Sets/Shared/MySet/Subset1") == null);
                Assert.IsTrue(dir.Get("/Sets/Shared/MySet") != null);

                folder = dir.Get("/Sets/Shared/AnotherSet/Subset");
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(folder.Name == "Subset");

                msg = dir.Get("/Sets/Shared/AnotherSet/Subset/Message");
                Assert.IsTrue(msg != null);
                Assert.IsTrue(msg is Message);

                dir.Remove(msgId);

                Assert.IsTrue(dir.Get("/Sets/Shared/AnotherSet/Subset/Message") == null);
                Assert.IsTrue(dir.Get("/Sets/Shared/AnotherSet/Subset") == null);
                Assert.IsTrue(dir.Get("/Sets/Shared/AnotherSet") != null);
            }
        }

        #endregion

        #region GetAncestor

        [Test]
        public void GetAncestor()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set("MyAncestorSet", typeof(MyMessage).FullName, 1)
                            .Folder("Subset")
                                .Message("Message", BlobFormat.TextXml, new MyMessage { Id = 1, Name = "Message" })
                            .End()
                        .End();

                DirectoryEntry set = dir.GetAncestor("/Sets/Shared/MyAncestorSet/Subset/Message", 0, DirectoryEntryTypes.Set);
                Assert.IsTrue(set != null);
                Assert.IsTrue(set is Set);
                Assert.IsTrue(dir.GetPathById(set.Id) == "/Sets/Shared/MyAncestorSet");

                DirectoryEntryInfo setInfo = dir.GetAncestorInfo("/Sets/Shared/MyAncestorSet/Subset/Message", 0, DirectoryEntryTypes.Set);
                Assert.IsTrue(setInfo != null);
                Assert.IsTrue(setInfo.Type == DirectoryEntryTypes.Set);

                DirectoryEntry folder = dir.GetAncestor("/Sets/Shared/MyAncestorSet/Subset/Message", 2, DirectoryEntryTypes.Folder);
                Assert.IsTrue(folder != null);
                Assert.IsTrue(folder is Folder);
                Assert.IsTrue(dir.GetPathById(folder.Id) == "/Sets");

                DirectoryEntryInfo folderInfo = dir.GetAncestorInfo("/Sets/Shared/MyAncestorSet/Subset/Message", 2, DirectoryEntryTypes.Folder);
                Assert.IsTrue(folderInfo != null);
                Assert.IsTrue(folderInfo.Type == DirectoryEntryTypes.Folder);
            }
        }

        #endregion

        #region Cascade delete

        [Test]
        public void CascadeDelete()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set("MySet", typeof(MyMessage).FullName, 1)
                            .Folder("Subset")
                                .Message("Message", BlobFormat.TextXml, new MyMessage { Id = 1, Name = "Message" })
                            .End()
                        .End();

                var msgId = dir.GetInfo("/Sets/Shared/MySet/Subset/Message").Id;
                var subsetId = dir.GetInfo("/Sets/Shared/MySet/Subset").Id;

                dir.Remove("/Sets/Shared/MySet");

                Assert.IsFalse(dir.Exists(msgId, DateTime.MinValue));
                Assert.IsFalse(dir.Exists(subsetId, DateTime.MinValue));
            }
        }

        #endregion

        #region Children queries

        [Test]
        public void GetChildren()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set("MyParentSet", typeof(MyMessage).FullName, 0)
                            .Folder("Folder").End()
                        .End()
                    .End();

                var message = new MyMessage
                {
                    Id = 123,
                    Name = "Message"
                };

                #region Saving messages

                dir.Save
                (
                    "/Sets/Shared/MyParentSet",
                    new Message
                    {
                        Name = "M2",
                        Data = message.ToBlob(BlobFormat.BinXml)
                    }
                );

                Thread.Sleep(100);

                dir.Save
                (
                    "/Sets/Shared/MyParentSet",
                    new Message
                    {
                        Name = "M1",
                        Data = message.ToBlob(BlobFormat.BinXml)
                    }
                );

                Thread.Sleep(100);

                dir.Save
                (
                    "/Sets/Shared/MyParentSet",
                    new Message
                    {
                        Name = "M3",
                        Data = message.ToBlob(BlobFormat.BinXml)
                    }
                );

                #endregion

                IList<DirectoryEntry> entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.Default, SortOrder.Asc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M2");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M3");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.Default, SortOrder.Desc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M3");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M2");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.Name, SortOrder.Asc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M1");
                Assert.IsTrue(entries[1].Name == "M2");
                Assert.IsTrue(entries[2].Name == "M3");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.Name, SortOrder.Desc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M3");
                Assert.IsTrue(entries[1].Name == "M2");
                Assert.IsTrue(entries[2].Name == "M1");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.CreatedOn, SortOrder.Asc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M2");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M3");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.CreatedOn, SortOrder.Desc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M3");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M2");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.UpdatedOn, SortOrder.Asc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M2");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M3");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.UpdatedOn, SortOrder.Desc, 0, 3);

                Assert.IsTrue(entries.Count == 3);
                Assert.IsTrue(entries[0].Name == "M3");
                Assert.IsTrue(entries[1].Name == "M1");
                Assert.IsTrue(entries[2].Name == "M2");

                entries =
                    dir.GetChildren("/Sets/Shared/MyParentSet", DirectoryEntryTypes.Message, SortProperty.Default, SortOrder.Asc, 1, 2);
                Assert.IsTrue(entries.Count == 1);
                Assert.IsTrue(entries[0].Name == "M1");
            }
        }

        [Test]
        public void CountChildren()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set("MySet10", typeof(MyMessage).FullName, 0).End()
                        .Set("MySet20", typeof(MyMessage).FullName, 0).End()
                        .Folder("Folder10").End()
                        .Folder("Folder20").End()
                    .End();

                Assert.IsTrue(dir.CountChildren("/Sets/Shared", DirectoryEntryTypes.Set) == 2);
                Assert.IsTrue(dir.CountChildren("/Sets/Shared", DirectoryEntryTypes.Folder) == 2);
            }
        }

        #endregion

        #region Exists

        [Test]
        public void Exists()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Services")
                        .Folder("System10")
                            .Folder("Subsystem").End()
                        .End()
                    .End();

                Assert.IsTrue(dir.Exists("/Services/System10/Subsystem", DateTime.MinValue));
                Assert.IsTrue(!dir.Exists("/Services/System10/Subsystem", DateTime.MaxValue));
                Assert.IsTrue(!dir.Exists("/Services/System10/Subsystem_", DateTime.MinValue));
            }
        }

        #endregion

        #region Authorization

        [Test]
        public void Authorization()
        {
            var dir = CreateDirectory();

            if (!_testAuthorization)
            {
                return;
            }

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                new DirectoryBuilder(dir)
                    .Root("/Sets/Shared")
                        .Set
                        (
                            "ForApp1", 
                            typeof(string).AssemblyQualifiedName, 
                            0,
                            acl =>
                                acl
                                    .GrantRead("Application")
                                    .GrantCreateChildren("Application/App1")
                        ).End()
                        .Set
                        (
                            "ForApp2", 
                            typeof(string).AssemblyQualifiedName, 
                            0,
                            acl =>
                                acl
                                    .GrantRead("Application")
                                    .GrantCreateChildren("Application/App2")
                        ).End()
                        .Set
                        (
                            "ForAllApps", 
                            typeof(string).AssemblyQualifiedName, 
                            0,
                            acl =>
                                acl
                                    .GrantRead("Application")
                                    .GrantCreateChildren("Application")
                        ).End()
                        .Set
                        (
                            "Invisible",
                            typeof(string).AssemblyQualifiedName,
                            0
                        )
                            .Folder
                            (
                                "Subset",
                                acl =>
                                    acl
                                        .GrantRead("Application")
                            ).End()
                        .End()
                    .End();

                AuthorizationProvider
                    .ClearRoles()
                    .AddRole("Application/App1");

                Assert.Throws
                (
                    typeof(FaultException<DirectoryFault>), 
                    () => dir.Save("/Sets/Shared/ForApp2", new Folder { Name = "Subset1" })
                );
                Assert.DoesNotThrow
                (
                    () => dir.Save("/Sets/Shared/ForApp1", new Folder { Name = "Subset1" })
                );
                Assert.DoesNotThrow
                (
                    () => dir.Save("/Sets/Shared/ForAllApps", new Folder { Name = "Subset1" })
                );

                AuthorizationProvider
                    .ClearRoles()
                    .AddRole("Application");
                
                Assert.DoesNotThrow
                (
                    () => dir.Save("/Sets/Shared/ForApp2", new Folder { Name = "Subset1" })
                );
                Assert.DoesNotThrow
                (
                    () => dir.Save("/Sets/Shared/ForAllApps", new Folder { Name = "Subset2" })
                );

                Assert.NotNull(dir.Get("/Sets/Shared/Invisible/Subset"));
                Assert.IsNull(dir.Get("/Sets/Shared/Invisible"));
                Assert.IsNull(dir.GetInfo("/Sets/Shared/Invisible"));
                Assert.AreEqual(3, dir.CountChildren("/Sets/Shared", DirectoryEntryTypes.Set));
                Assert.AreEqual(3, dir.GetChildren("/Sets/Shared", DirectoryEntryTypes.Set, SortProperty.Default, SortOrder.Asc, 0, 100).Count);
                Assert.AreEqual(3, dir.GetChildrenInfo("/Sets/Shared", DirectoryEntryTypes.Set, SortProperty.Default, SortOrder.Asc, 0, 100).Count);
                Assert.IsNull(dir.GetAncestor("/Sets/Shared/Invisible/Subset", 0, DirectoryEntryTypes.Set));
                Assert.IsNull(dir.GetAncestorInfo("/Sets/Shared/Invisible/Subset", 0, DirectoryEntryTypes.Set));
            }
        }

        #endregion

        #region Fields persistence

        [DataContract]
        private class MyProcessState : Workflow.Process
        {
            [DataMember]
            public string DataMember { get; set; }

            public void DefineProcess(IProcessBuilder process)
            {
            }
        }

        [Test]
        public void FieldsPersistence()
        {
            var dir = CreateDirectory();

            AuthorizationProvider
                .ClearRoles()
                .AddRole("Administrator");

            using (new TransactionScope(TransactionScopeOption.Required, TransactionOptions))
            {
                var state =
                    new MyProcessState
                    {
                        DataMember = "Data"
                    };

                string pid;

                new DirectoryBuilder(dir)
                    .Root("/Assemblies")
                        .Assembly("MyAssembly", new byte[] { 1, 2, 3 }).End()
                    .End()
                    .Root("/Services")
                        .ServiceContainer("MyServiceContainer", (1).ToBlob(BlobFormat.BinXml)).End()
                    .End()
                    .Root("/Workflows")
                        .Workflow("MyWorkflow", "MyWorkflowDefinition", new [] { "proc1", "proc2", "proc3" }).End()
                    .End()
                    .Root("/Processors")
                        .Processor("MyProcessor", (1).ToBlob(BlobFormat.BinXml))
                            .Process("Workflow", out pid)
                                .State(state).End()
                            .End()
                        .End()
                    .End()
                    .Root("/Processes")
                        .Link("MyLink", pid).End()
                    .End()
                    .Root("/Sets/Shared")
                        .Set("MySet", typeof(int).AssemblyQualifiedName, 10)
                            .Message("MyMessage", BlobFormat.BinXml, 1).End()
                        .End()
                    .End()
                    .Root("/Roles/Application")
                        .Role("MyApp", new [] { "user1", "user2", "user3" }).End()
                    .End();

                var assembly = dir.Get("/Assemblies/MyAssembly") as Assembly;
                Assert.IsNotNull(assembly);
                Assert.IsTrue(assembly.Image[0] == 1 && assembly.Image[1] == 2 && assembly.Image[2] == 3);

                assembly = dir.GetChildren("/Assemblies", DirectoryEntryTypes.Assembly, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Assembly;
                Assert.IsNotNull(assembly);
                Assert.IsTrue(assembly.Image[0] == 1 && assembly.Image[1] == 2 && assembly.Image[2] == 3);


                var serviceContainer = dir.Get("/Services/MyServiceContainer") as ServiceContainer;
                Assert.IsNotNull(serviceContainer);
                Assert.IsTrue(serviceContainer.Configuration.To<int>() == 1);

                serviceContainer = dir.GetChildren("/Services", DirectoryEntryTypes.ServiceContainer, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as ServiceContainer;
                Assert.IsNotNull(serviceContainer);
                Assert.IsTrue(serviceContainer.Configuration.To<int>() == 1);


                var workflow = dir.Get("/Workflows/MyWorkflow") as Services.Data.Workflow;
                Assert.IsNotNull(workflow);
                Assert.AreEqual("MyWorkflowDefinition", workflow.DefinitionType);
                Assert.IsTrue
                (
                    workflow.PreferredProcessors.Contains("proc1") && 
                    workflow.PreferredProcessors.Contains("proc2") && 
                    workflow.PreferredProcessors.Contains("proc3")
                );

                workflow = dir.GetChildren("/Workflows", DirectoryEntryTypes.Workflow, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Services.Data.Workflow;
                Assert.IsNotNull(workflow);
                Assert.AreEqual("MyWorkflowDefinition", workflow.DefinitionType);
                Assert.IsTrue
                (
                    workflow.PreferredProcessors.Contains("proc1") &&
                    workflow.PreferredProcessors.Contains("proc2") &&
                    workflow.PreferredProcessors.Contains("proc3")
                );


                var processor = dir.Get("/Processors/MyProcessor") as Processor;
                Assert.IsNotNull(processor);
                Assert.IsTrue(processor.Configuration.To<int>() == 1);

                processor = dir.GetChildren("/Processors", DirectoryEntryTypes.Processor, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Processor;
                Assert.IsNotNull(processor);


                var process = dir.Get(pid) as Process;
                Assert.IsNotNull(process);
                Assert.AreEqual("Workflow", process.Workflow);

                process = dir.GetChildren("/Processors/MyProcessor", DirectoryEntryTypes.Process, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Process;
                Assert.IsNotNull(process);
                Assert.AreEqual("Workflow", process.Workflow);

                var stateEnt = dir.GetChildren("/Processors/MyProcessor/" + pid, DirectoryEntryTypes.State, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as State;
                Assert.IsNotNull(stateEnt);
                var stateEntState = stateEnt.Data.To<MyProcessState>();
                Assert.IsTrue(stateEntState.DataMember == "Data");

                stateEnt = dir.Get(stateEnt.Id) as State;
                Assert.IsNotNull(stateEnt);
                stateEntState = stateEnt.Data.To<MyProcessState>();
                Assert.IsTrue(stateEntState.DataMember == "Data");


                var link = dir.Get("/Processes/MyLink") as Link;
                Assert.IsNotNull(link);
                Assert.AreEqual(pid, link.Target);

                link = dir.GetChildren("/Processes", DirectoryEntryTypes.Link, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Link;
                Assert.IsNotNull(link);
                Assert.AreEqual(pid, link.Target);


                var set = dir.Get("/Sets/Shared/MySet") as Set;
                Assert.IsNotNull(set);
                Assert.AreEqual(typeof(int).AssemblyQualifiedName, set.MessageType);
                Assert.AreEqual(10, set.Capacity);

                set = dir.GetChildren("/Sets/Shared", DirectoryEntryTypes.Set, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Set;
                Assert.IsNotNull(set);
                Assert.AreEqual(typeof(int).AssemblyQualifiedName, set.MessageType);
                Assert.AreEqual(10, set.Capacity);


                var message = dir.Get("/Sets/Shared/MySet/MyMessage") as Message;
                Assert.IsNotNull(message);
                Assert.AreEqual(1, message.Data.To<int>());

                message = dir.GetChildren("/Sets/Shared/MySet", DirectoryEntryTypes.Message, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Message;
                Assert.IsNotNull(message);
                Assert.AreEqual(1, message.Data.To<int>());


                var role = dir.Get("/Roles/Application/MyApp") as Role;
                Assert.IsNotNull(role);
                Assert.IsTrue(role.Users.Contains("user1") && role.Users.Contains("user2") && role.Users.Contains("user3"));

                role = dir.GetChildren("/Roles/Application", DirectoryEntryTypes.Role, SortProperty.Default, SortOrder.Asc, 0, 1)[0] as Role;
                Assert.IsNotNull(role);
                Assert.IsTrue(role.Users.Contains("user1") && role.Users.Contains("user2") && role.Users.Contains("user3"));
            }
        }

        #endregion
    }
}
